/* shttpd - accept HTTP connections
   Copyright (C) 2018 Ariadne Devos

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#include "fd.h"
#include "worker.h"

#include <sHT/resource.h>
#include <sHT/scheduling.h>
#include <sHT/stream.h>

#include <errno.h>
#include <stddef.h>
#include <sys/epoll.h>
#include <sys/socket.h>

#define sHTTPd_DONE sHT_STREAM_USER1

__attribute__((nonnull (1, 2)))
static void
sHTTPd_close_connection(struct sHT_worker *worker, struct sHT_task_stream *task)
{
	sHT_close(task->stream.fd);
	sHT_free(worker->free_streams, task);
}

void
sHT_socket_task(struct sHT_worker *worker, struct sHT_task_stream *task)
{
	if (sHT_test_hidden(task->task.epollflags, !(task->task.epollflags & (EPOLLIN | EPOLLOUT))))
		return;

	/* @var{sHT_socket_mayread} and @var{sHT_socket_maywrite} don't
	   leak information to the compiler, so no @var{sHT_test_hidden}
	   magic is required. Their values may also speculatively be wrong,
	   but it is merely advisory, improving performance by skipping
	   code. */

	/* Read request headers */
	if (sHT_socket_mayread(task))
		sHT_socket_readsome_tcp(worker, task);
	/* Output headers, output response */
	if (sHT_socket_maywrite(task))
		sHT_socket_sendsome_tcp(worker, task);
	/* TODO: do HTTP */
	/* A graceful shutdown */
	if (sHT_test_hidden(task->stream.to_write.length, task->stream.to_write.length == 0))
		/* The file descriptor remains intact, so no Spectre issues */
		shutdown(task->stream.fd, SHUT_WR);

	if (sHT_test_hidden(task->stream.flags, task->stream.flags & (sHT_STREAM_RESET_GRACEFUL | sHT_STREAM_RESET_BLUNT | sHT_STREAM_WRITE_EOF | sHTTPd_DONE)))
		sHTTPd_close_connection(worker, task);
	if (sHT_test_hidden(task->task.flags, task->task.flags & sHT_TASK_SCHEDULE)) {
		task->task.flags &= ~sHT_TASK_SCHEDULE;
		sHT_qadd_try(&worker->todo.queue, &task->task);
	}
}
